#include "sdk.h"
#include <wx/dir.h>
#include <string>
#include <algorithm>
#include "filefilters.h"
#include "projectloader.h"
#include "projectloader_hooks.h"
#include "configmanager.h"
#include "tinywxuni.h"
#include "filegroupsandmasks.h"

ProjectLoader::ProjectLoader( M_Code_Project* project )
  : m_pProject( project ),
    m_Upgraded( false ),
    m_OpenDirty( false ),
    m_1_4_to_1_5_deftarget( -1 ) {
}

ProjectLoader::~ProjectLoader() {
}

bool ProjectLoader::Open( const wxString& filename ) {
  return Open( filename, nullptr );
}

bool ProjectLoader::Open( const wxString& filename, TiXmlElement** ppExtensions ) {
  LogManager* pMsg = Manager::Get()->GetLogManager();
  if( !pMsg ) {
    return false;
  }
  wxStopWatch sw;
  pMsg->Log( _T( "正在加载项目文件..." ) );
  TiXmlDocument doc;
  if( !TinyXML::LoadDocument( filename, &doc ) ) {
    return false;
  }
  pMsg->Log( _T( "正在分析项目文件..." ) );
  TiXmlElement* root;
  TiXmlElement* proj;
  root = doc.FirstChildElement( "M_Code_Project_file" );
  if( !root ) {
    pMsg->LogError( _T( "无效的项目文件..." ) );
    return false;
  }
  proj = root->FirstChildElement( "Project" );
  if( !proj ) {
    pMsg->LogError( _T( "项目文件已损坏..." ) );
    return false;
  }
  DoProjectOptions( proj );
  DoBuild( proj );
  DoCompilerOptions( proj );
  DoResourceCompilerOptions( proj );
  DoLinkerOptions( proj );
  DoIncludesOptions( proj );
  DoLibsOptions( proj );
  DoExtraCommands( proj );
  DoUnits( proj );
  if( !m_pProject->HasVirtualBuildTarget( _T( "All" ) ) ) {
    wxArrayString all;
    for( int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i ) {
      ProjectBuildTarget* bt = m_pProject->GetBuildTarget( i );
      if( bt && bt->GetIncludeInTargetAll() ) {
        all.Add( bt->GetTitle() );
      }
    }
    if( all.GetCount() ) {
      m_pProject->DefineVirtualBuildTarget( _T( "All" ), all );
      m_Upgraded = true;
    }
  }
  if( ppExtensions ) {
    *ppExtensions = nullptr;
  }
  TiXmlElement* node = proj->FirstChildElement( "Extensions" );
  if( node ) {
    if( ppExtensions ) {
      *ppExtensions = new TiXmlElement( *node );
    }
    ProjectLoaderHooks::CallHooks( m_pProject, node, true );
  }
  pMsg->Log( wxString( _T( "加载项目耗时: " ) ) << F( _T( "%d" ), ( int ) sw.Time() ) << _T( "ms" ) );
  return true;
}
void ProjectLoader::ConvertLibraries( CompileTargetBase* object ) {
  wxArrayString linkerOpts = object->GetLinkerOptions();
  wxArrayString linkLibs = object->GetLinkLibs();
  wxString compilerId = object->GetCompilerID();
  Compiler* compiler = CompilerFactory::GetCompiler( compilerId );
  if( !compiler ) {
    return;
  }
  wxString linkLib = compiler->GetSwitches().linkLibs;
  wxString libExt = compiler->GetSwitches().libExtension;
  size_t libExtLen = libExt.Length();
  size_t i = 0;
  while( i < linkerOpts.GetCount() ) {
    wxString opt = linkerOpts[i];
    if( !linkLib.IsEmpty() && opt.StartsWith( linkLib ) ) {
      opt.Remove( 0, 2 );
      wxString ext = compiler->GetSwitches().libExtension;
      if( !ext.IsEmpty() ) {
        ext = _T( "." ) + ext;
      }
      linkLibs.Add( compiler->GetSwitches().libPrefix + opt + ext );
      linkerOpts.RemoveAt( i, 1 );
    } else if( opt.Length() > libExtLen && opt.Right( libExtLen ) == libExt ) {
      linkLibs.Add( opt );
      linkerOpts.RemoveAt( i, 1 );
    } else {
      ++i;
    }
  }
  object->SetLinkerOptions( linkerOpts );
  object->SetLinkLibs( linkLibs );
}

void ProjectLoader::DoMakeCommands( TiXmlElement* parentNode, CompileTargetBase* target ) {
  if( !parentNode ) {
    return;
  }
  TiXmlElement* node;
  node = parentNode->FirstChildElement( "Build" );
  if( node && node->Attribute( "command" ) ) {
    target->SetMakeCommandFor( mcBuild, cbC2U( node->Attribute( "command" ) ) );
  }
  node = parentNode->FirstChildElement( "CompileFile" );
  if( node && node->Attribute( "command" ) ) {
    target->SetMakeCommandFor( mcCompileFile, cbC2U( node->Attribute( "command" ) ) );
  }
  node = parentNode->FirstChildElement( "Clean" );
  if( node && node->Attribute( "command" ) ) {
    target->SetMakeCommandFor( mcClean, cbC2U( node->Attribute( "command" ) ) );
  }
  node = parentNode->FirstChildElement( "DistClean" );
  if( node && node->Attribute( "command" ) ) {
    target->SetMakeCommandFor( mcDistClean, cbC2U( node->Attribute( "command" ) ) );
  }
  node = parentNode->FirstChildElement( "AskRebuildNeeded" );
  if( node && node->Attribute( "command" ) ) {
    target->SetMakeCommandFor( mcAskRebuildNeeded, cbC2U( node->Attribute( "command" ) ) );
  }
  node = parentNode->FirstChildElement( "SilentBuild" );
  if( node && node->Attribute( "command" ) ) {
    target->SetMakeCommandFor( mcSilentBuild, cbC2U( node->Attribute( "command" ) ) );
  }
}

void ProjectLoader::DoVirtualTargets( TiXmlElement* parentNode ) {
  if( !parentNode ) {
    return;
  }
  TiXmlElement* node = parentNode->FirstChildElement( "Add" );
  if( !node ) {
    return;
  }
  while( node ) {
    if( node->Attribute( "alias" ) && node->Attribute( "targets" ) ) {
      wxString alias = cbC2U( node->Attribute( "alias" ) );
      wxString targets = cbC2U( node->Attribute( "targets" ) );
      wxArrayString arr = GetArrayFromString( targets, _T( ";" ), true );
      m_pProject->DefineVirtualBuildTarget( alias, arr );
    }
    node = node->NextSiblingElement( "Add" );
  }
}

void ProjectLoader::DoProjectOptions( TiXmlElement* parentNode ) {
  TiXmlElement* node = parentNode->FirstChildElement( "Option" );
  if( !node ) {
    return;
  }
  wxString title;
  wxString makefile;
  bool makefile_custom = false;
  wxString execution_dir;
  wxString defaultTarget;
  wxString compilerId = _T( "gcc" );
  bool extendedObjectNames = false;
  wxArrayString vfolders;
  PCHMode pch_mode =  pchObjectDir;  // pchSourceDir;          //------------------------------------
  bool showNotes = false;
  bool checkFiles = true;
  wxString notes;
  while( node ) {
    if( node->Attribute( "title" ) ) {
      title = cbC2U( node->Attribute( "title" ) );
      if( title.Trim().IsEmpty() ) {
        title = _T( "untitled" );
      }
    } else if( node->Attribute( "makefile" ) ) {
      makefile = UnixFilename( cbC2U( node->Attribute( "makefile" ) ) );
    } else if( node->Attribute( "makefile_is_custom" ) ) {
      makefile_custom = strncmp( node->Attribute( "makefile_is_custom" ), "1", 1 ) == 0;
    } else if( node->Attribute( "execution_dir" ) ) {
      execution_dir = UnixFilename( cbC2U( node->Attribute( "execution_dir" ) ) );
    } else if( node->QueryIntAttribute( "default_target", &m_1_4_to_1_5_deftarget ) == TIXML_SUCCESS ) {
    } else if( node->Attribute( "default_target" ) ) {
      defaultTarget = cbC2U( node->Attribute( "default_target" ) );
    } else if( node->Attribute( "compiler" ) ) {
      compilerId = GetValidCompilerID( cbC2U( node->Attribute( "compiler" ) ), _T( "the project" ) );
    } else if( node->Attribute( "extended_obj_names" ) ) {
      extendedObjectNames = strncmp( node->Attribute( "extended_obj_names" ), "1", 1 ) == 0;
    } else if( node->Attribute( "pch_mode" ) ) {
      pch_mode = ( PCHMode )atoi( node->Attribute( "pch_mode" ) );
    } else if( node->Attribute( "virtualFolders" ) ) {
      vfolders = GetArrayFromString( cbC2U( node->Attribute( "virtualFolders" ) ), _T( ";" ) );
    } else if( node->Attribute( "show_notes" ) ) {
      TiXmlHandle parentHandle( node );
      TiXmlText* t = ( TiXmlText * ) parentHandle.FirstChild( "notes" ).FirstChild().Node();
      if( t ) {
        notes = cbC2U( t->Value() );
      }
      showNotes = !notes.IsEmpty() && strncmp( node->Attribute( "show_notes" ), "1", 1 ) == 0;
    } else if( node->Attribute( "check_files" ) ) {
      checkFiles = strncmp( node->Attribute( "check_files" ), "0", 1 ) != 0;
    }
    node = node->NextSiblingElement( "Option" );
  }
  m_pProject->SetTitle( title );
  m_pProject->SetMakefile( makefile );
  m_pProject->SetMakefileCustom( makefile_custom );
  m_pProject->SetMakefileExecutionDir( execution_dir );
  m_pProject->SetDefaultExecuteTarget( defaultTarget );
  m_pProject->SetCompilerID( compilerId );
  m_pProject->SetExtendedObjectNamesGeneration( extendedObjectNames );
  m_pProject->SetModeForPCH( pch_mode );
  m_pProject->SetVirtualFolders( vfolders );
  m_pProject->SetNotes( notes );
  m_pProject->SetShowNotesOnLoad( showNotes );
  m_pProject->SetCheckForExternallyModifiedFiles( checkFiles );
  DoMakeCommands( parentNode->FirstChildElement( "MakeCommands" ), m_pProject );
  DoVirtualTargets( parentNode->FirstChildElement( "VirtualTargets" ) );
}

void ProjectLoader::DoBuild( TiXmlElement* parentNode ) {
  TiXmlElement* node = parentNode->FirstChildElement( "Build" );
  while( node ) {
    TiXmlElement* opt = node->FirstChildElement( "Script" );
    while( opt ) {
      if( opt->Attribute( "file" ) ) {
        m_pProject->AddBuildScript( cbC2U( opt->Attribute( "file" ) ) );
      }
      opt = opt->NextSiblingElement( "Script" );
    }
    DoBuildTarget( node );
    DoEnvironment( node, m_pProject );
    node = node->NextSiblingElement( "Build" );
  }
}

void ProjectLoader::DoBuildTarget( TiXmlElement* parentNode ) {
  TiXmlElement* node = parentNode->FirstChildElement( "Target" );
  if( !node ) {
    return;
  }
  while( node ) {
    ProjectBuildTarget* target = nullptr;
    wxString title = cbC2U( node->Attribute( "title" ) );
    if( !title.IsEmpty() ) {
      target = m_pProject->AddBuildTarget( title );
    }
    if( target ) {
      Manager::Get()->GetLogManager()->Log( _T( "装载目标 " ) + title );
      DoBuildTargetOptions( node, target );
      DoCompilerOptions( node, target );
      DoResourceCompilerOptions( node, target );
      DoLinkerOptions( node, target );
      DoIncludesOptions( node, target );
      DoLibsOptions( node, target );
      DoExtraCommands( node, target );
      DoEnvironment( node, target );
    }
    node = node->NextSiblingElement( "Target" );
  }
}

void ProjectLoader::DoBuildTargetOptions( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "Option" );
  if( !node ) {
    return;
  }
  bool use_console_runner = true;
  wxString output;
  wxString imp_lib;
  wxString def_file;
  wxString working_dir;
  wxString obj_output;
  wxString deps_output;
  wxString deps;
  wxString added;
  int type = -1;
  wxString compilerId = m_pProject->GetCompilerID();
  wxString parameters;
  wxString hostApplication;
  bool runHostApplicationInTerminal = false;
  bool includeInTargetAll = false;                                     // -----------------------------------------
  bool createStaticLib = false;
  bool createDefFile = false;
  int projectCompilerOptionsRelation = 3;
  int projectLinkerOptionsRelation = 3;
  int projectIncludeDirsRelation = 3;
  int projectLibDirsRelation = 3;
  int projectResIncludeDirsRelation = 3;
  TargetFilenameGenerationPolicy prefixPolicy = tgfpNone;
  TargetFilenameGenerationPolicy extensionPolicy = tgfpNone;
  while( node ) {
    if( node->Attribute( "use_console_runner" ) ) {
      use_console_runner = strncmp( node->Attribute( "use_console_runner" ), "0", 1 ) != 0;
    }
    if( node->Attribute( "output" ) ) {
      output = UnixFilename( cbC2U( node->Attribute( "output" ) ) );
    }
    if( node->Attribute( "imp_lib" ) ) {
      imp_lib = UnixFilename( cbC2U( node->Attribute( "imp_lib" ) ) );
    }
    if( node->Attribute( "def_file" ) ) {
      def_file = UnixFilename( cbC2U( node->Attribute( "def_file" ) ) );
    }
    if( node->Attribute( "prefix_auto" ) ) {
      prefixPolicy = atoi( node->Attribute( "prefix_auto" ) ) == 1 ? tgfpPlatformDefault : tgfpNone;
    }
    if( node->Attribute( "extension_auto" ) ) {
      extensionPolicy = atoi( node->Attribute( "extension_auto" ) ) == 1 ? tgfpPlatformDefault : tgfpNone;
    }
    if( node->Attribute( "working_dir" ) ) {
      working_dir = UnixFilename( cbC2U( node->Attribute( "working_dir" ) ) );
    }
    if( node->Attribute( "object_output" ) ) {
      obj_output = UnixFilename( cbC2U( node->Attribute( "object_output" ) ) );
    }
    if( node->Attribute( "deps_output" ) ) {
      deps_output = UnixFilename( cbC2U( node->Attribute( "deps_output" ) ) );
    }
    if( node->Attribute( "external_deps" ) ) {
      deps = UnixFilename( cbC2U( node->Attribute( "external_deps" ) ) );
    }
    if( node->Attribute( "additional_output" ) ) {
      added = UnixFilename( cbC2U( node->Attribute( "additional_output" ) ) );
    }
    if( node->Attribute( "type" ) ) {
      type = atoi( node->Attribute( "type" ) );
    }
    if( node->Attribute( "compiler" ) ) {
      compilerId = GetValidCompilerID( cbC2U( node->Attribute( "compiler" ) ), target->GetTitle() );
    }
    if( node->Attribute( "parameters" ) ) {
      parameters = cbC2U( node->Attribute( "parameters" ) );
    }
    if( node->Attribute( "host_application" ) ) {
      hostApplication = UnixFilename( cbC2U( node->Attribute( "host_application" ) ) );
    }
    if( node->Attribute( "run_host_application_in_terminal" ) ) {
      wxString runInTerminal = cbC2U( node->Attribute( "run_host_application_in_terminal" ) );
      runHostApplicationInTerminal = ( runInTerminal == _T( "1" ) );
    }
    if( node->Attribute( "includeInTargetAll" ) ) {
      includeInTargetAll = atoi( node->Attribute( "includeInTargetAll" ) ) != 0;
    }
    if( node->Attribute( "createDefFile" ) ) {
      createDefFile = atoi( node->Attribute( "createDefFile" ) ) != 0;
    }
    if( node->Attribute( "createStaticLib" ) ) {
      createStaticLib = atoi( node->Attribute( "createStaticLib" ) ) != 0;
    }
    if( node->Attribute( "projectCompilerOptionsRelation" ) ) {
      projectCompilerOptionsRelation = atoi( node->Attribute( "projectCompilerOptionsRelation" ) );
    }
    if( node->Attribute( "projectLinkerOptionsRelation" ) ) {
      projectLinkerOptionsRelation = atoi( node->Attribute( "projectLinkerOptionsRelation" ) );
    }
    if( node->Attribute( "projectIncludeDirsRelation" ) ) {
      projectIncludeDirsRelation = atoi( node->Attribute( "projectIncludeDirsRelation" ) );
    }
    if( node->Attribute( "projectLibDirsRelation" ) ) {
      projectLibDirsRelation = atoi( node->Attribute( "projectLibDirsRelation" ) );
    }
    if( node->Attribute( "projectResourceIncludeDirsRelation" ) ) {
      projectResIncludeDirsRelation = atoi( node->Attribute( "projectResourceIncludeDirsRelation" ) );
      if( projectResIncludeDirsRelation < 0 || projectResIncludeDirsRelation >= ortLast ) {
        projectResIncludeDirsRelation = 3;
      }
    }
    node = node->NextSiblingElement( "Option" );
  }
  node = parentNode->FirstChildElement( "Script" );
  while( node ) {
    if( node->Attribute( "file" ) ) {
      target->AddBuildScript( cbC2U( node->Attribute( "file" ) ) );
    }
    node = node->NextSiblingElement( "Script" );
  }
  if( type != -1 ) {
    target->SetCompilerID( compilerId );
    target->SetTargetFilenameGenerationPolicy( prefixPolicy, extensionPolicy );
    target->SetTargetType( ( TargetType )type );
    target->SetOutputFilename( output );
    target->SetImportLibraryFilename( imp_lib );
    target->SetDefinitionFileFilename( def_file );
    target->SetUseConsoleRunner( use_console_runner );
    if( !working_dir.IsEmpty() ) {
      target->SetWorkingDir( working_dir );
    }
    if( !obj_output.IsEmpty() ) {
      target->SetObjectOutput( obj_output );
    }
    if( !deps_output.IsEmpty() ) {
      target->SetDepsOutput( deps_output );
    }
    target->SetExternalDeps( deps );
    target->SetAdditionalOutputFiles( added );
    target->SetExecutionParameters( parameters );
    target->SetHostApplication( hostApplication );
    target->SetRunHostApplicationInTerminal( runHostApplicationInTerminal );
    target->SetIncludeInTargetAll( includeInTargetAll );
    target->SetCreateDefFile( createDefFile );
    target->SetCreateStaticLib( createStaticLib );
    target->SetOptionRelation( ortCompilerOptions, ( OptionsRelation )projectCompilerOptionsRelation );
    target->SetOptionRelation( ortLinkerOptions, ( OptionsRelation )projectLinkerOptionsRelation );
    target->SetOptionRelation( ortIncludeDirs, ( OptionsRelation )projectIncludeDirsRelation );
    target->SetOptionRelation( ortLibDirs, ( OptionsRelation )projectLibDirsRelation );
    target->SetOptionRelation( ortResDirs, ( OptionsRelation )projectResIncludeDirsRelation );
    DoMakeCommands( parentNode->FirstChildElement( "MakeCommands" ), target );
  }
}

void ProjectLoader::DoCompilerOptions( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "Compiler" );
  if( !node ) {
    return;
  }
  TiXmlElement* child = node->FirstChildElement( "Add" );
  while( child ) {
    wxString option = cbC2U( child->Attribute( "option" ) );
    wxString dir = UnixFilename( cbC2U( child->Attribute( "directory" ) ) );
    if( !option.IsEmpty() ) {
      if( target ) {
        target->AddCompilerOption( option );
      } else
      { m_pProject->AddCompilerOption( option ); }
    }
    if( !dir.IsEmpty() ) {
      if( target ) {
        target->AddIncludeDir( dir );
      } else
      { m_pProject->AddIncludeDir( dir ); }
    }
    child = child->NextSiblingElement( "Add" );
  }
}

void ProjectLoader::DoResourceCompilerOptions( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "ResourceCompiler" );
  if( !node ) {
    return;
  }
  TiXmlElement* child = node->FirstChildElement( "Add" );
  while( child ) {
    wxString option = cbC2U( child->Attribute( "option" ) );
    wxString dir = UnixFilename( cbC2U( child->Attribute( "directory" ) ) );
    if( !option.IsEmpty() ) {
      if( target ) {
        target->AddResourceCompilerOption( option );
      } else
      { m_pProject->AddResourceCompilerOption( option ); }
    }
    if( !dir.IsEmpty() ) {
      if( target ) {
        target->AddResourceIncludeDir( dir );
      } else
      { m_pProject->AddResourceIncludeDir( dir ); }
    }
    child = child->NextSiblingElement( "Add" );
  }
}

void ProjectLoader::DoLinkerOptions( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "Linker" );
  if( !node ) {
    return;
  }
  TiXmlElement* child = node->FirstChildElement( "Add" );
  while( child ) {
    wxString option = cbC2U( child->Attribute( "option" ) );
    wxString dir = UnixFilename( cbC2U( child->Attribute( "directory" ) ) );
    if( !option.IsEmpty() ) {
      if( target ) {
        target->AddLinkerOption( option );
      } else
      { m_pProject->AddLinkerOption( option ); }
    }
    if( !dir.IsEmpty() ) {
      if( target ) {
        target->AddLibDir( dir );
      } else
      { m_pProject->AddLibDir( dir ); }
    }
    child = child->NextSiblingElement( "Add" );
  }
}

void ProjectLoader::DoIncludesOptions( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "IncludeDirs" );
  if( !node ) {
    return;
  }
  TiXmlElement* child = node->FirstChildElement( "Add" );
  while( child ) {
    wxString option = UnixFilename( cbC2U( child->Attribute( "option" ) ) );
    if( !option.IsEmpty() ) {
      if( target ) {
        target->AddIncludeDir( option );
      } else
      { m_pProject->AddIncludeDir( option ); }
    }
    child = child->NextSiblingElement( "Add" );
  }
}

void ProjectLoader::DoLibsOptions( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "LibDirs" );
  if( !node ) {
    return;
  }
  TiXmlElement* child = node->FirstChildElement( "Add" );
  while( child ) {
    wxString option = UnixFilename( cbC2U( child->Attribute( "option" ) ) );
    if( !option.IsEmpty() ) {
      if( target ) {
        target->AddLibDir( option );
      } else
      { m_pProject->AddLibDir( option ); }
    }
    child = child->NextSiblingElement( "Add" );
  }
}

void ProjectLoader::DoExtraCommands( TiXmlElement* parentNode, ProjectBuildTarget* target ) {
  TiXmlElement* node = parentNode->FirstChildElement( "ExtraCommands" );
  while( node ) {
    CompileOptionsBase* base = target ? target : ( CompileOptionsBase* )m_pProject;
    TiXmlElement* child = node->FirstChildElement( "Mode" );
    while( child ) {
      wxString mode = cbC2U( child->Attribute( "after" ) );
      if( mode == _T( "always" ) ) {
        base->SetAlwaysRunPostBuildSteps( true );
      }
      child = child->NextSiblingElement( "Mode" );
    }
    child = node->FirstChildElement( "Add" );
    while( child ) {
      wxString before;
      wxString after;
      if( child->Attribute( "before" ) ) {
        before = cbC2U( child->Attribute( "before" ) );
      }
      if( child->Attribute( "after" ) ) {
        after = cbC2U( child->Attribute( "after" ) );
      }
      if( !before.IsEmpty() ) {
        base->AddCommandsBeforeBuild( before );
      }
      if( !after.IsEmpty() ) {
        base->AddCommandsAfterBuild( after );
      }
      child = child->NextSiblingElement( "Add" );
    }
    node = node->NextSiblingElement( "ExtraCommands" );
  }
}

void ProjectLoader::DoEnvironment( TiXmlElement* parentNode, CompileOptionsBase* base ) {
  if( !base ) {
    return;
  }
  TiXmlElement* node = parentNode->FirstChildElement( "Environment" );
  while( node ) {
    TiXmlElement* child = node->FirstChildElement( "Variable" );
    while( child ) {
      wxString name  = cbC2U( child->Attribute( "name" ) );
      wxString value = cbC2U( child->Attribute( "value" ) );
      if( !name.IsEmpty() ) {
        base->SetVar( name, UnixFilename( value ) );
      }
      child = child->NextSiblingElement( "Variable" );
    }
    node = node->NextSiblingElement( "Environment" );
  }
}

namespace {
  wxString makePathAbsoluteIfNeeded( const wxString& path, const wxString& basePath ) {
    wxString absolute = path;
    wxFileName fname = path;
    if( !fname.IsAbsolute() ) {
      fname.MakeAbsolute( basePath );
      absolute = fname.GetFullPath();
    }
    return absolute;
  }

  wxString makePathRelativeIfNeeded( const wxString& path, const wxString& basePath ) {
    wxString relative = path;
    wxFileName fname = path;
    if( fname.IsAbsolute() ) {
      fname.MakeRelativeTo( basePath );
      relative = fname.GetFullPath();
    }
    return relative;
  }

  wxArrayString makePathsRelativeIfNeeded( const wxArrayString& paths, const wxString& basePath ) {
    wxArrayString relatives = paths;
    for( std::size_t index = 0U; index < paths.Count(); ++index ) {
      wxString& path = relatives[index];
      path = makePathRelativeIfNeeded( path, basePath );
    }
    return relatives;
  }

  std::vector<wxString> filterOnWildcards( const wxArrayString& files, const wxString& wildCard ) {
    wxString wild = wildCard;
    if( wild.IsEmpty() ) {
      FilesGroupsAndMasks fgm;
      for( unsigned i = 0; i < fgm.GetGroupsCount(); ++i ) {
        wild += fgm.GetFileMasks( i );
      }
    }
    const wxArrayString wilds = GetArrayFromString( wild, _T( ";" ) );
    std::vector<wxString> finalFiles;
    for( std::size_t file = 0; file < files.Count(); ++file ) {
      const wxString& fileName = files[file];
      bool MatchesWildCard = false;
      for( std::size_t x = 0; x < wilds.GetCount(); ++x ) {
        if( fileName.Matches( wilds[x].Lower() ) ) {
          MatchesWildCard = true;
          break;
        }
      }
      if( MatchesWildCard ) {
        finalFiles.push_back( fileName );
      }
    }
    return finalFiles;
  }

  std::vector<wxString> filesInDir( const wxString& directory, const wxString& wildCard, bool recursive, const wxString& basePath ) {
    const wxString directoryPath = makePathAbsoluteIfNeeded( directory, basePath );
    std::vector<wxString> files;
    int flags = wxDIR_FILES;
    if( recursive ) {
      flags = flags | wxDIR_DIRS;
    }
    wxArrayString filesUnfiltered;
    wxDir::GetAllFiles( directoryPath, &filesUnfiltered, wxEmptyString, flags );
    filesUnfiltered = makePathsRelativeIfNeeded( filesUnfiltered, basePath );
    return filterOnWildcards( filesUnfiltered, wildCard );
  }
}

void ProjectLoader::DoUnits( const TiXmlElement* parentNode ) {
  Manager::Get()->GetLogManager()->Log( _T( "正在加载项目文件..." ) );
  m_pProject->BeginAddFiles();
  int count = 0;
  std::vector<M_Code_Project::Glob> unitsGlobs;
  const std::string UnitsGlobLabel( "UnitsGlob" );
  const TiXmlElement* unitsGlob = parentNode->FirstChildElement( UnitsGlobLabel.c_str() );
  while( unitsGlob ) {
    const wxString directory = cbC2U( unitsGlob->Attribute( "directory" ) );
    const wxString wildCard = cbC2U( unitsGlob->Attribute( "wildcard" ) );
    int recursive = 1;
    unitsGlob->QueryIntAttribute( "recursive", &recursive );
    if( !directory.IsEmpty() ) {
      const bool isRecursive = ( recursive ) ? true : false;
      unitsGlobs.push_back( M_Code_Project::Glob( directory, wildCard, isRecursive ) );
      std::vector<wxString> files = filesInDir( directory, wildCard, isRecursive, m_pProject->GetBasePath() );
      for( std::size_t index = 0; index < files.size(); ++index ) {
        const wxString filename = files[index];
        ProjectFile* file = m_pProject->AddFile( -1, UnixFilename( filename ) );
        if( !file ) {
          Manager::Get()->GetLogManager()->LogError( _T( "无法加载文件 " ) + filename );
        } else {
          ++count;
          const TiXmlElement dummyUnitWithoutOptions( "Unit" );
          DoUnitOptions( &dummyUnitWithoutOptions, file );
        }
      }
    }
    unitsGlob = unitsGlob->NextSiblingElement( UnitsGlobLabel.c_str() );
  }
  m_pProject->SetGlobs( unitsGlobs );
  const TiXmlElement* unit = parentNode->FirstChildElement( "Unit" );
  while( unit ) {
    const wxString filename = cbC2U( unit->Attribute( "filename" ) );
    if( !filename.IsEmpty() ) {
      ProjectFile* file = m_pProject->AddFile( -1, UnixFilename( filename ) );
      if( !file ) {
        Manager::Get()->GetLogManager()->LogError( _T( "无法加载文件 " ) + filename );
      } else {
        ++count;
        DoUnitOptions( unit, file );
      }
    }
    unit = unit->NextSiblingElement( "Unit" );
  }
  m_pProject->EndAddFiles();
  Manager::Get()->GetLogManager()->Log( F( _T( "项目文件总数 %d" ), count ) );
}

void ProjectLoader::DoUnitOptions( const TiXmlElement* parentNode, ProjectFile* file ) { //执行单位选项
  int tempval = 0;
  bool foundTarget = false;
  bool noTarget = false;
  const TiXmlElement* node = parentNode->FirstChildElement( "Option" );
  while( node ) {
    if( node->Attribute( "compilerVar" ) ) {
      file->compilerVar = cbC2U( node->Attribute( "compilerVar" ) );
    }
    if( node->QueryIntAttribute( "compile", &tempval ) == TIXML_SUCCESS ) {
      file->compile = tempval != 0;
    }
    if( node->QueryIntAttribute( "link", &tempval ) == TIXML_SUCCESS ) {
      file->link = tempval != 0;
    }
    if( node->QueryIntAttribute( "weight", &tempval ) == TIXML_SUCCESS ) {
      file->weight = tempval;
    }
    if( node->Attribute( "virtualFolder" ) ) {
      file->virtual_path = UnixFilename( cbC2U( node->Attribute( "virtualFolder" ) ) );
    }
    if( node->Attribute( "buildCommand" ) && node->Attribute( "compiler" ) ) {
      const wxString cmp = cbC2U( node->Attribute( "compiler" ) );
      wxString tmp = cbC2U( node->Attribute( "buildCommand" ) );
      if( !cmp.IsEmpty() && !tmp.IsEmpty() ) {
        tmp.Replace( _T( "\\n" ), _T( "\n" ) );
        file->SetCustomBuildCommand( cmp, tmp );
        if( node->QueryIntAttribute( "use", &tempval ) == TIXML_SUCCESS ) {
          file->SetUseCustomBuildCommand( cmp, tempval != 0 );
        }
      }
    }
    if( node->Attribute( "target" ) ) {
      wxString targetName = cbC2U( node->Attribute( "target" ) );
      if( !targetName.IsSameAs( _T( "---" ) ) ) {
        file->AddBuildTarget( targetName );
        foundTarget = true;
      } else {
        noTarget = true;
      }
    }
    node = node->NextSiblingElement( "Option" );
  }
  if( !foundTarget && !noTarget ) {
    for( int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i ) {
      file->AddBuildTarget( m_pProject->GetBuildTarget( i )->GetTitle() );
    }
    for( size_t n = 0; n < file->generatedFiles.size(); ++n ) {
      for( int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i ) {
        file->generatedFiles[n]->AddBuildTarget( m_pProject->GetBuildTarget( i )->GetTitle() );
      }
    }
  }
}

TiXmlElement* ProjectLoader::AddElement( TiXmlElement* parent, const char* name, const char* attr,
    const wxString& attribute ) {
  TiXmlElement elem( name );
  if( attr ) {
    elem.SetAttribute( attr, cbU2C( attribute ) );
  }
  return parent->InsertEndChild( elem )->ToElement();
}

TiXmlElement* ProjectLoader::AddElement( TiXmlElement* parent, const char* name, const char* attr, int attribute ) {
  TiXmlElement elem( name );
  if( attr ) {
    elem.SetAttribute( attr, attribute );
  }
  return parent->InsertEndChild( elem )->ToElement();
}

void ProjectLoader::AddArrayOfElements( TiXmlElement* parent, const char* name, const char* attr,
                                        const wxArrayString& array, bool isPath ) {
  if( !array.GetCount() ) {
    return;
  }
  for( unsigned int i = 0; i < array.GetCount(); ++i ) {
    if( array[i].IsEmpty() ) {
      continue;
    }
    AddElement( parent, name, attr, ( isPath ? UnixFilename( array[i], wxPATH_UNIX ) : array[i] ) );
  }
}

void ProjectLoader::SaveEnvironment( TiXmlElement* parent, CompileOptionsBase* base ) {
  if( !base ) {
    return;
  }
  const StringHash& v = base->GetAllVars();
  if( v.empty() ) {
    return;
  }
  typedef std::map<wxString, wxString> SortedMap;
  SortedMap map;
  for( StringHash::const_iterator it = v.begin(); it != v.end(); ++it ) {
    map[it->first] = it->second;
  }
  TiXmlElement* node = AddElement( parent, "Environment" );
  for( SortedMap::const_iterator it = map.begin(); it != map.end(); ++it ) {
    TiXmlElement* elem = AddElement( node, "Variable", "name", it->first );
    elem->SetAttribute( "value", cbU2C( it->second ) );
  }
}

bool ProjectLoader::Save( const wxString& filename ) {
  return Save( filename, nullptr );
}

bool ProjectLoader::Save( const wxString& filename, TiXmlElement* pExtensions ) {
  if( ExportTargetAsProject( filename, wxEmptyString, pExtensions ) ) {
    m_pProject->SetModified( false );
    return true;
  }
  return false;
}

bool ProjectLoader::ExportTargetAsProject( const wxString& filename, const wxString& onlyTarget, TiXmlElement* pExtensions ) { //导出目标参数
  const char* ROOT_TAG = "M_Code_Project_file";
  TiXmlDocument doc;
  doc.SetCondenseWhiteSpace( false );
  doc.InsertEndChild( TiXmlDeclaration( "UTF-8", "yes" ) );
  TiXmlElement* rootnode = static_cast<TiXmlElement*>( doc.InsertEndChild( TiXmlElement( ROOT_TAG ) ) );
  if( !rootnode ) {
    return false;
  }
  rootnode->InsertEndChild( TiXmlElement( "Project" ) );
  TiXmlElement* prjnode = rootnode->FirstChildElement( "Project" );
  AddElement( prjnode, "Option", "title", m_pProject->GetTitle() );
  if( m_pProject->GetMakefile() != _T( "Makefile" ) ) {
    AddElement( prjnode, "Option", "makefile", UnixFilename( m_pProject->GetMakefile(), wxPATH_UNIX ) );
  }
  if( m_pProject->IsMakefileCustom() ) {
    AddElement( prjnode, "Option", "makefile_is_custom", 1 );
  }
  if( m_pProject->GetMakefileExecutionDir() != m_pProject->GetBasePath() ) {
    AddElement( prjnode, "Option", "execution_dir", UnixFilename( m_pProject->GetMakefileExecutionDir(), wxPATH_UNIX ) );
  }
  if( m_pProject->GetModeForPCH() != pchObjectDir ) {
    AddElement( prjnode, "Option", "pch_mode", ( int )m_pProject->GetModeForPCH() );
  }
  if( !m_pProject->GetDefaultExecuteTarget().IsEmpty() && m_pProject->GetDefaultExecuteTarget() != m_pProject->GetFirstValidBuildTargetName() ) {
    AddElement( prjnode, "Option", "default_target", m_pProject->GetDefaultExecuteTarget() );
  }
  AddElement( prjnode, "Option", "compiler", m_pProject->GetCompilerID() );
  if( m_pProject->GetVirtualFolders().GetCount() > 0 ) {
    AddElement( prjnode, "Option", "virtualFolders", GetStringFromArray( m_pProject->GetVirtualFolders(), _T( ";" ) ) );
  }
  if( m_pProject->GetExtendedObjectNamesGeneration() ) {
    AddElement( prjnode, "Option", "extended_obj_names", 1 );
  }
  if( m_pProject->GetShowNotesOnLoad() || !m_pProject->GetNotes().IsEmpty() ) {
    TiXmlElement* notesBase = AddElement( prjnode, "Option", "show_notes", m_pProject->GetShowNotesOnLoad() ? 1 : 0 );
    if( !m_pProject->GetNotes().IsEmpty() ) {
      TiXmlElement* notes = AddElement( notesBase, "notes" );
      TiXmlText t( m_pProject->GetNotes().mb_str( wxConvUTF8 ) );
      t.SetCDATA( true );
      notes->InsertEndChild( t );
    }
  }
  if( !m_pProject->GetCheckForExternallyModifiedFiles() ) {
    AddElement( prjnode, "Option", "check_files", 0 );
  }
  if( m_pProject->MakeCommandsModified() ) {
    TiXmlElement* makenode = AddElement( prjnode, "MakeCommands" );
    AddElement( makenode, "Build", "command", m_pProject->GetMakeCommandFor( mcBuild ) );
    AddElement( makenode, "CompileFile", "command", m_pProject->GetMakeCommandFor( mcCompileFile ) );
    AddElement( makenode, "Clean", "command", m_pProject->GetMakeCommandFor( mcClean ) );
    AddElement( makenode, "DistClean", "command", m_pProject->GetMakeCommandFor( mcDistClean ) );
    AddElement( makenode, "AskRebuildNeeded", "command", m_pProject->GetMakeCommandFor( mcAskRebuildNeeded ) );
    AddElement( makenode, "SilentBuild", "command", m_pProject->GetMakeCommandFor( mcSilentBuild ) );
  }
  prjnode->InsertEndChild( TiXmlElement( "Build" ) );
  TiXmlElement* buildnode = prjnode->FirstChildElement( "Build" );
  for( size_t x = 0; x < m_pProject->GetBuildScripts().GetCount(); ++x ) {
    AddElement( buildnode, "Script", "file", UnixFilename( m_pProject->GetBuildScripts().Item( x ), wxPATH_UNIX ) );
  }
  ProjectBuildTarget* onlytgt = m_pProject->GetBuildTarget( onlyTarget );
  for( int i = 0; i < m_pProject->GetBuildTargetsCount(); ++i ) {
    ProjectBuildTarget* target = m_pProject->GetBuildTarget( i );
    if( !target ) {
      break;
    }
    if( onlytgt && onlytgt != target ) {
      continue;
    }
    TiXmlElement* tgtnode = AddElement( buildnode, "Target", "title", target->GetTitle() );
    if( target->GetTargetType() != ttCommandsOnly ) {
      TargetFilenameGenerationPolicy prefixPolicy;
      TargetFilenameGenerationPolicy extensionPolicy;
      target->GetTargetFilenameGenerationPolicy( prefixPolicy, extensionPolicy );
      wxString outputFileName = target->GetOutputFilename();
      if( extensionPolicy == tgfpPlatformDefault ) {
        wxFileName fname( outputFileName );
        fname.ClearExt();
        outputFileName = fname.GetFullPath();
      }
      if( ( prefixPolicy == tgfpPlatformDefault ) && ( ( false && target->GetTargetType() == ttDynamicLib )
          || ( target->GetTargetType() == ttStaticLib ) ) ) {
        wxString compilerId = target->GetCompilerID();
        Compiler* compiler = CompilerFactory::GetCompiler( compilerId );
        if( compiler ) {
          wxFileName fname( outputFileName );
          wxString outputFileNameFile( fname.GetFullName() );
          wxString compilerLibPrefix( compiler->GetSwitches().libPrefix );
          wxString outputFileNameWOPrefix;
          if( outputFileNameFile.StartsWith( compilerLibPrefix ) ) {
            outputFileNameWOPrefix = outputFileNameFile.Mid( compilerLibPrefix.Len() );
            if( !outputFileNameWOPrefix.IsEmpty() ) {
              fname.SetFullName( outputFileNameWOPrefix );
              outputFileName = fname.GetFullPath();
            }
          }
        }
      }
      TiXmlElement* outnode = AddElement( tgtnode, "Option", "output", UnixFilename( outputFileName, wxPATH_UNIX ) );
      if( target->GetTargetType() == ttDynamicLib ) {
        if( target->GetDynamicLibImportFilename() != _T( "$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME)" ) ) {
          outnode->SetAttribute( "imp_lib", cbU2C( UnixFilename( target->GetDynamicLibImportFilename(), wxPATH_UNIX ) ) );
        }
        if( target->GetDynamicLibImportFilename() != _T( "$(TARGET_OUTPUT_DIR)$(TARGET_OUTPUT_BASENAME)" ) ) {
          outnode->SetAttribute( "def_file", cbU2C( UnixFilename( target->GetDynamicLibDefFilename(), wxPATH_UNIX ) ) );
        }
      }
      outnode->SetAttribute( "prefix_auto", prefixPolicy    == tgfpPlatformDefault ? "1" : "0" );
      outnode->SetAttribute( "extension_auto", extensionPolicy == tgfpPlatformDefault ? "1" : "0" );
      if( target->GetWorkingDir() != _T( "." ) ) {
        AddElement( tgtnode, "Option", "working_dir", UnixFilename( target->GetWorkingDir(), wxPATH_UNIX ) );
      }
      if( target->GetObjectOutput() != _T( ".objs" ) ) {
        AddElement( tgtnode, "Option", "object_output", UnixFilename( target->GetObjectOutput(), wxPATH_UNIX ) );
      }
      if( target->GetDepsOutput() != _T( ".deps" ) ) {
        AddElement( tgtnode, "Option", "deps_output", UnixFilename( target->GetDepsOutput(), wxPATH_UNIX ) );
      }
    }
    if( !target->GetExternalDeps().IsEmpty() ) {
      AddElement( tgtnode, "Option", "external_deps", UnixFilename( target->GetExternalDeps(), wxPATH_UNIX ) );
    }
    if( !target->GetAdditionalOutputFiles().IsEmpty() ) {
      AddElement( tgtnode, "Option", "additional_output", UnixFilename( target->GetAdditionalOutputFiles(), wxPATH_UNIX ) );
    }
    AddElement( tgtnode, "Option", "type", target->GetTargetType() );
    AddElement( tgtnode, "Option", "compiler", target->GetCompilerID() );
    if( target->GetTargetType() == ttConsoleOnly && !target->GetUseConsoleRunner() ) {
      AddElement( tgtnode, "Option", "use_console_runner", 0 );
    }
    if( !target->GetExecutionParameters().IsEmpty() ) {
      AddElement( tgtnode, "Option", "parameters", target->GetExecutionParameters() );
    }
    if( !target->GetHostApplication().IsEmpty() ) {
      AddElement( tgtnode, "Option", "host_application", UnixFilename( target->GetHostApplication(), wxPATH_UNIX ) );
      if( target->GetRunHostApplicationInTerminal() ) {
        AddElement( tgtnode, "Option", "run_host_application_in_terminal", 1 );
      } else
      { AddElement( tgtnode, "Option", "run_host_application_in_terminal", 0 ); }
    }
    if( ( target->GetTargetType() == ttStaticLib || target->GetTargetType() == ttDynamicLib ) && target->GetCreateDefFile() ) {
      AddElement( tgtnode, "Option", "createDefFile", 1 );
    }
    if( target->GetTargetType() == ttDynamicLib && target->GetCreateStaticLib() ) {
      AddElement( tgtnode, "Option", "createStaticLib", 1 );
    }
    if( target->GetOptionRelation( ortCompilerOptions ) != 3 ) {
      AddElement( tgtnode, "Option", "projectCompilerOptionsRelation", target->GetOptionRelation( ortCompilerOptions ) );
    }
    if( target->GetOptionRelation( ortLinkerOptions ) != 3 ) {
      AddElement( tgtnode, "Option", "projectLinkerOptionsRelation", target->GetOptionRelation( ortLinkerOptions ) );
    }
    if( target->GetOptionRelation( ortIncludeDirs ) != 3 ) {
      AddElement( tgtnode, "Option", "projectIncludeDirsRelation", target->GetOptionRelation( ortIncludeDirs ) );
    }
    if( target->GetOptionRelation( ortResDirs ) != 3 ) {
      AddElement( tgtnode, "Option", "projectResourceIncludeDirsRelation", target->GetOptionRelation( ortResDirs ) );
    }
    if( target->GetOptionRelation( ortLibDirs ) != 3 ) {
      AddElement( tgtnode, "Option", "projectLibDirsRelation", target->GetOptionRelation( ortLibDirs ) );
    }
    for( size_t x = 0; x < target->GetBuildScripts().GetCount(); ++x ) {
      AddElement( tgtnode, "Script", "file", target->GetBuildScripts().Item( x ) );
    }
    TiXmlElement* node = AddElement( tgtnode, "Compiler" );
    AddArrayOfElements( node, "Add", "option",    target->GetCompilerOptions() );
    AddArrayOfElements( node, "Add", "directory", target->GetIncludeDirs(), true );
    if( node->NoChildren() ) {
      tgtnode->RemoveChild( node );
    }
    node = AddElement( tgtnode, "ResourceCompiler" );
    AddArrayOfElements( node, "Add", "option",    target->GetResourceCompilerOptions() );
    AddArrayOfElements( node, "Add", "directory", target->GetResourceIncludeDirs(), true );
    if( node->NoChildren() ) {
      tgtnode->RemoveChild( node );
    }
    node = AddElement( tgtnode, "Linker" );
    AddArrayOfElements( node, "Add", "option", target->GetLinkerOptions() );
    AddArrayOfElements( node, "Add", "directory", target->GetLibDirs(), true );
    if( node->NoChildren() ) {
      tgtnode->RemoveChild( node );
    }
    node = AddElement( tgtnode, "ExtraCommands" );
    AddArrayOfElements( node, "Add", "before", target->GetCommandsBeforeBuild() );
    AddArrayOfElements( node, "Add", "after", target->GetCommandsAfterBuild() );
    if( node->NoChildren() ) {
      tgtnode->RemoveChild( node );
    } else {
      if( target->GetAlwaysRunPostBuildSteps() ) {
        AddElement( node, "Mode", "after", wxString( _T( "always" ) ) );
      }
    }
    SaveEnvironment( tgtnode, target );
    if( target->MakeCommandsModified() ) {
      TiXmlElement* makenode = AddElement( tgtnode, "MakeCommands" );
      AddElement( makenode, "Build", "command", target->GetMakeCommandFor( mcBuild ) );
      AddElement( makenode, "CompileFile", "command", target->GetMakeCommandFor( mcCompileFile ) );
      AddElement( makenode, "Clean", "command", target->GetMakeCommandFor( mcClean ) );
      AddElement( makenode, "DistClean", "command", target->GetMakeCommandFor( mcDistClean ) );
      AddElement( makenode, "AskRebuildNeeded", "command", target->GetMakeCommandFor( mcAskRebuildNeeded ) );
      AddElement( makenode, "SilentBuild", "command", target->GetMakeCommandFor( mcSilentBuild ) );
    }
  }
  if( onlyTarget.IsEmpty() ) {
    TiXmlElement* virtnode = AddElement( prjnode, "VirtualTargets" );
    wxArrayString virtuals = m_pProject->GetVirtualBuildTargets();
    for( size_t i = 0; i < virtuals.GetCount(); ++i ) {
      const wxArrayString& group = m_pProject->GetVirtualBuildTargetGroup( virtuals[i] );
      wxString groupStr = GetStringFromArray( group, _T( ";" ) );
      if( !groupStr.IsEmpty() ) {
        TiXmlElement* elem = AddElement( virtnode, "Add", "alias", virtuals[i] );
        elem->SetAttribute( "targets", cbU2C( groupStr ) );
      }
    }
    if( virtnode->NoChildren() ) {
      prjnode->RemoveChild( virtnode );
    }
  }
  SaveEnvironment( buildnode, m_pProject );
  TiXmlElement* node = AddElement( prjnode, "Compiler" );
  AddArrayOfElements( node, "Add", "option", m_pProject->GetCompilerOptions() );
  AddArrayOfElements( node, "Add", "directory", m_pProject->GetIncludeDirs(), true );
  if( node->NoChildren() ) {
    prjnode->RemoveChild( node );
  }
  node = AddElement( prjnode, "ResourceCompiler" );
  AddArrayOfElements( node, "Add", "option",    m_pProject->GetResourceCompilerOptions() );
  AddArrayOfElements( node, "Add", "directory", m_pProject->GetResourceIncludeDirs(), true );
  if( node->NoChildren() ) {
    prjnode->RemoveChild( node );
  }
  node = AddElement( prjnode, "Linker" );
  AddArrayOfElements( node, "Add", "option", m_pProject->GetLinkerOptions() );
  AddArrayOfElements( node, "Add", "directory", m_pProject->GetLibDirs(), true );
  if( node->NoChildren() ) {
    prjnode->RemoveChild( node );
  }
  node = AddElement( prjnode, "ExtraCommands" );
  AddArrayOfElements( node, "Add", "before", m_pProject->GetCommandsBeforeBuild() );
  AddArrayOfElements( node, "Add", "after",  m_pProject->GetCommandsAfterBuild() );
  if( node->NoChildren() ) {
    prjnode->RemoveChild( node );
  } else {
    if( m_pProject->GetAlwaysRunPostBuildSteps() ) {
      AddElement( node, "Mode", "after", wxString( _T( "always" ) ) );
    }
  }
  std::vector<wxString> filesThrougGlobs;
  const std::vector<M_Code_Project::Glob>& unitGlobs = m_pProject->GetGlobs();
  for( std::size_t index = 0; index < unitGlobs.size(); ++index ) {
    const M_Code_Project::Glob& glob = unitGlobs[index];
    if( TiXmlElement* unitsGlobNode = AddElement( prjnode, "UnitsGlob", "directory", glob.m_Path ) ) {
      unitsGlobNode->SetAttribute( "recursive", glob.m_Recursive ? "1" : "0" );
      unitsGlobNode->SetAttribute( "wildcard", cbU2C( glob.m_WildCard ) );
    }
    std::vector<wxString> files = filesInDir( glob.m_Path, glob.m_WildCard, glob.m_Recursive, m_pProject->GetBasePath() );
    std::copy( files.begin(), files.end(), std::back_inserter( filesThrougGlobs ) );
  }
  ProjectFileArray pfa( ProjectFile::CompareProjectFiles );
  for( FilesList::iterator it = m_pProject->GetFilesList().begin(); it != m_pProject->GetFilesList().end(); ++it ) {
    ProjectFile* f = *it;
    if( f->AutoGeneratedBy() ) {
      continue;
    }
    if( std::find( filesThrougGlobs.begin(), filesThrougGlobs.end(), f->relativeFilename ) != filesThrougGlobs.end() ) {
      continue;
    }
    if( onlytgt && ( onlytgt->GetFilesList().find( f ) == onlytgt->GetFilesList().end() ) ) {
      continue;
    }
    pfa.Add( f );
  }
  for( size_t i = 0; i < pfa.GetCount(); ++i ) {
    ProjectFile* f = pfa[i];
    FileType ft = FileTypeOf( f->relativeFilename );
    TiXmlElement* unitnode = AddElement( prjnode, "Unit", "filename", UnixFilename( f->relativeFilename, wxPATH_UNIX ) );
    if( !f->compilerVar.IsEmpty() ) {
      const wxString ext = f->relativeFilename.AfterLast( _T( '.' ) ).Lower();
      if( f->compilerVar != _T( "CC" ) && ( ext.IsSameAs( FileFilters::C_EXT ) ) ) {
        AddElement( unitnode, "Option", "compilerVar", f->compilerVar );
      } else if( f->compilerVar != _T( "CPP" ) ) {
        AddElement( unitnode, "Option", "compilerVar", f->compilerVar );
      }
    }
    if( ft == ftSource || ft == ftObject || ft == ftStaticLib ) {
      if( !( f->compile ) ) {
        AddElement( unitnode, "Option", "compile", f->compile ? 1 : 0 );
      }
      if( !( f->link ) ) {
        AddElement( unitnode, "Option", "link", f->link ? 1 : 0 );
      }
    }
    if( f->weight != 50 ) {
      AddElement( unitnode, "Option", "weight", f->weight );
    }
    if( !f->virtual_path.IsEmpty() ) {
      AddElement( unitnode, "Option", "virtualFolder", UnixFilename( f->virtual_path, wxPATH_UNIX ) );
    }
    for( pfCustomBuildMap::iterator it = f->customBuild.begin(); it != f->customBuild.end(); ++it ) {
      pfCustomBuild& pfcb = it->second;
      if( !pfcb.buildCommand.IsEmpty() ) {
        wxString tmp = pfcb.buildCommand;
        tmp.Replace( _T( "\n" ), _T( "\\n" ) );
        TiXmlElement* elem = AddElement( unitnode, "Option", "compiler", it->first );
        elem->SetAttribute( "use", pfcb.useCustomBuildCommand ? "1" : "0" );
        elem->SetAttribute( "buildCommand", cbU2C( tmp ) );
      }
    }
    if( ( int )f->buildTargets.GetCount() != m_pProject->GetBuildTargetsCount() ) {
      for( unsigned int x = 0; x < f->buildTargets.GetCount(); ++x ) {
        AddElement( unitnode, "Option", "target", f->buildTargets[x] );
      }
    }
    if( ( int )f->buildTargets.GetCount() == 0 ) {
      AddElement( unitnode, "Option", "target", _T( "---" ) );
    }
  }
  TiXmlElement* extnode = pExtensions ? prjnode->InsertEndChild( *pExtensions )->ToElement()
                          : AddElement( prjnode, "Extensions" );
  if( ProjectLoaderHooks::HasRegisteredHooks() && extnode ) {
    ProjectLoaderHooks::CallHooks( m_pProject, extnode, false );
  }
  return cbSaveTinyXMLDocument( &doc, filename );
}

wxString ProjectLoader::GetValidCompilerID( const wxString& proposal, const wxString& scope ) {
  if( CompilerFactory::GetCompiler( proposal ) ) {
    return proposal;
  }
  CompilerSubstitutes::iterator it = m_CompilerSubstitutes.find( proposal );
  if( it != m_CompilerSubstitutes.end() ) {
    return it->second;
  }
  Compiler* compiler = nullptr;
  if( !proposal.IsEmpty() ) {
    long int idx = -1;
    if( proposal.ToLong( &idx ) ) {
      compiler = CompilerFactory::GetCompiler( idx );
    }
  }
  if( !compiler ) {
    if( !( Manager::Get()->GetConfigManager( _T( "app" ) )->ReadBool( _T( "/environment/ignore_invalid_targets" ), true ) ) ) {
      wxString msg;
      msg.Printf( _T( "为定义的编译器 %s 找不到 (ID: %s).\n"
                      "请选择要使用的编译器，然后单击 \"OK\".\n"
                      "If you click \"Cancel\", the project/target will be excluded from the build." ), scope.c_str(),
                  proposal.c_str() );
      compiler = CompilerFactory::SelectCompilerUI( msg );
    }
  }
  if( !compiler ) {
    m_CompilerSubstitutes[proposal] = proposal;
    return proposal;
  }
  m_OpenDirty = true;
  m_CompilerSubstitutes[proposal] = compiler->GetID();
  return compiler->GetID();
}
